#include <time.h>
#include <stdio.h>
#include <errno.h>
#include <fcntl.h>
#include <errno.h>
#include <stdlib.h>
#include <getopt.h>
#include <dirent.h>
#include <unistd.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include "log.h"
#include "args.h"
#define DEFAULT_TIME_LEN 64
#define DEFAULT_WORKING_DIR "/var/tmp/camera"
#define DEFAULT_MOUNT_DIR "/mnt/camera"
#define GOOVER_PATH_CMD "ls -l -R %s > /dev/null"

static struct option __options[] = 
{
	{EXCLUDE_LONG, required_argument, NULL, EXCLUDE_SHORT},
	{FILTER_LONG, required_argument, NULL, FILTER_SHORT},
	{TO_LONG, required_argument, NULL, TO_SHORT},
	{NAME_LONG, required_argument, NULL, NAME_SHORT},
	{MOUNT_LONG, required_argument, NULL, MOUNT_SHORT},
	//{LOG_LONG, required_argument, NULL, LOG_SHORT},
	{APPEND_LONG, required_argument, NULL, APPEND_SHORT},
	{HELP_LONG, no_argument, NULL, HELP_SHORT}
};

args ag = {NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,0};

void __go_over_path_recur(const char* path) 
{
	char cmd[CMD_LEN];
	memset(cmd, 0, CMD_LEN);
	sprintf(cmd, GOOVER_PATH_CMD, path);
	system(cmd);
}

bool __prepare_working_dir() 
{
	int ret = mkdir(DEFAULT_WORKING_DIR, 
			S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH);

	if (ret!=0 && errno!=EEXIST) return false;
	return true;
}

void usage() {
	printf("Usage: camera <dirs to be shot> [-e excluded folder]\n \
      [-f filter] [-t dir where target file to be saved]\n \
      [-n target file name] [-m location where target file mount to]\n \
      [-a append space] [-h help]\n");
}

bool __is_valid_path(const char* path, bool mustDir) 
{
	struct stat st;
	if (stat(path, &st) == -1) return false;
	return mustDir ? S_ISDIR(st.st_mode) : true;	
}

bool is_valid_space(const char* space)
{	
	if (!space || !strlen(space)) return false;
	FOR(i,0,strlen(space)-1) {
		if (space[i]<'0'||space[i]>'9') return false;
	}
	char unit = space[strlen(space)-1];
	switch(unit)
	{
	case 'k':
	case 'K':
	case 'm':
	case 'M':
	case 'g':
	case 'G':
		return true;
	default:
		return false;
	} 
	return false;
}

bool parse_args(int argc, char** argv)
{
	char c= -1;
	int pidx = 0;

	if (log_to) free(log_to);
	log_to = (char*)malloc(strlen(LOG_TO)+1);
	memset(log_to, 0, strlen(LOG_TO)+1);
	strcpy(log_to, LOG_TO);
	int log_fd = open(log_to, O_CREAT|O_WRONLY|O_APPEND);
	if (log_fd < 0) CAMERA_EXIT("Failed to open log file");
	set_log_fd(log_fd);

	const char* pstr = ALL_SHORT;
	while ((c=getopt_long(argc,argv,pstr,__options,&pidx)) != -1)
		switch(c) {
		case EXCLUDE_SHORT:
			if (__is_valid_path(optarg, true)) {
				if (exclude_path_tail) 
					exclude_path_tail = append_node(exclude_path_tail, optarg);
				else exclude_path_head = exclude_path_tail = append_node(NULL, optarg);
			} else {
				CAMERA_LOG("Invalid exclude path: %s\n", optarg);
				return false; 
			}		
			break;			
		case FILTER_SHORT: 
			if (filter_tail) filter_tail = append_node(filter_tail, optarg);
			else filter_head = filter_tail = append_node(NULL, optarg);
			break;			
		case TO_SHORT:
			if (__is_valid_path(optarg, true)) set_save_to(&ag, optarg);
			else {
				CAMERA_LOG("Invalid save path: %s\n", optarg);
				return false;
			}
			break;			
		case NAME_SHORT:
			set_name(&ag, optarg); 
			break;			
		case MOUNT_SHORT:
			if (__is_valid_path(optarg, true)) set_mount_to(&ag, optarg);
			else {
				CAMERA_LOG("Invalid mount path: %s\n", optarg);
				return false;
			}
			break;			
			/*
			  case LOG_SHORT:
			  if (__is_valid_path(optarg, true)) {
			  set_log_to(&ag, optarg);
			  set_log_fd(open(log_to, O_CREAT|O_WRONLY));
			  }
			  else {
			  CAMERA_LOG("Invalid log path: %s\n", optarg);
			  return false;
			  }
			  break;			
			*/
		case APPEND_SHORT:
			if (is_valid_space(optarg)) set_append_space(&ag, optarg);
			else {
				CAMERA_LOG("Invalid surplus space: %s\n", optarg);
				return false;
			}
			break;			
		case HELP_SHORT:
		default:
			usage();
			exit(EXIT_SUCCESS);
		}
	
	while (optind < argc) {
		if (__is_valid_path(argv[optind], false)) {
			__go_over_path_recur(argv[optind]);
			if (camera_path_tail) 
				camera_path_tail = append_node(camera_path_tail, argv[optind]);
			else camera_path_head = camera_path_tail = append_node(NULL, argv[optind]);
		} else {
			CAMERA_LOG("Invalid target path: %s\n", argv[optind]);
			return false; 
		}	
		++optind;	
	}

	if (!camera_path_head) {
		CAMERA_LOG("Please specify target path!\n");
		return false;
	}

	if (!__prepare_working_dir()) {
		CAMERA_LOG("Failed to set working directory!\n");
		return false;
	}

	if (!save_to) {
		save_to = (char*)malloc(strlen(DEFAULT_WORKING_DIR)+1);		
		memset(save_to, 0, strlen(DEFAULT_WORKING_DIR)+1);
		memcpy(save_to, DEFAULT_WORKING_DIR, strlen(DEFAULT_WORKING_DIR));
	}

	time_t t;
	time(&t);
	char* timeStr = (char*)malloc(DEFAULT_TIME_LEN);
	memset(timeStr, 0, DEFAULT_TIME_LEN);
	strftime(timeStr, DEFAULT_TIME_LEN, "%F-%T", localtime(&t));

	if (!name) {
		name = (char*)malloc(DEFAULT_TIME_LEN+1);
		memset(name, 0, DEFAULT_TIME_LEN+1);
		memcpy(name, timeStr, strlen(timeStr));
	}

	if (!mount_to) {
		int lenHead = strlen(DEFAULT_MOUNT_DIR);
		int lenTail = strlen(name); 
		mount_to = (char*)malloc(lenHead + lenTail + 2);
		memset(mount_to, 0, lenHead+lenTail+2);
		memcpy(mount_to, DEFAULT_MOUNT_DIR, lenHead);
		memcpy(mount_to+strlen(mount_to), "/", 1);
		memcpy(mount_to+strlen(mount_to), name, lenTail);
	}
	
	if (timeStr) free(timeStr);

#ifdef TEST
	if (test_code == PARSE_ARGS) {
		print_args(&ag);
		exit(EXIT_SUCCESS);
	}
#endif

	return true;
}
